package com.yixing.tech.wms.service.impl;

import com.yixing.tech.common.base.BaseResponse;
import com.yixing.tech.common.base.BusinessException;
import com.yixing.tech.common.base.CustomAggregationOperation;
import com.yixing.tech.common.enums.YesOrNo;
import com.yixing.tech.common.entity.ManualInOutMaterial;
import com.yixing.tech.common.service.AbstractWmsService;
import com.yixing.tech.wms.entity.ManualStockWork;
import com.yixing.tech.wms.feigh.WcsService;
import com.yixing.tech.wms.param.ManualInParam;
import com.yixing.tech.wms.param.ManualOutParam;
import com.yixing.tech.wms.service.ManualInOutStockService;
import com.yixing.tech.wms.service.StockLocationService;
import com.yixing.tech.wms.service.WmsTaskService;
import com.yixing.tech.wms.stock.out.StockOutConstants;
import com.yixing.tech.wms.stock.out.callback.error.ErrorCodeEnum;
import com.yixing.tech.wms.stock.out.callback.error.TaskErrorCallbackVO;
import com.yixing.tech.wms.stock.out.domain.Task;
import com.yixing.tech.wms.stock.out.domain.vo.TaskMethod;
import com.yixing.tech.wms.stock.out.domain.vo.TaskType;
import com.yixing.tech.common.vo.s3.UserVO;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.ConvertOperators;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.yixing.tech.common.CommonConstant.*;

/**
 * @author yixing tech
 * @since 1.0.0
 */
@Slf4j
@Service
public class ManualInOutStockServiceImpl extends AbstractWmsService implements ManualInOutStockService {

    @Value("${tenant-id: 6204cb763da841718ddd1cf8}")
    private String tenantId;

    @Autowired
    private WmsTaskService wmsTaskService;
    @Autowired
    private StockLocationService stockLocationService;
    @Lazy
    @Autowired
    private WcsService wcsService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean out(ManualOutParam param) throws BusinessException {
        Document set = getSet();
        String storageArea;
        String pickArea;
        if (B.equals(param.getAbc())) {
            storageArea = set.getString("storageAreaB");
            // B类物料CTU任务下架任务需要固定指定终点库区为  B类原材料拣货入口库区
            pickArea = AreaCodeDict.bMatPickingEntrance.getText();
        } else {
            storageArea = set.getString("storageArea");
            pickArea = set.getString("pickArea");
        }

        String containerCode = param.getContainerCode();
        // 查询容器
        Document container = getContainer(containerCode);

        // 查看 开始的区域是否有容器存在
        Query query = Query.query(newCriteria().and("containerCode").is(containerCode)
                .and("area").is(storageArea));

        Document startLocation = mongoTemplate.findOne(query, Document.class, "entity_" + STOCK_LOCATION);
        if (startLocation == null) {
            throw new BusinessException(String.format("容器[%s]不在范围内", containerCode));
        }
        boolean lock = startLocation.getBoolean("lock");
        if (lock) {
            throw new BusinessException("该库位已被锁定");
        }

        return doMove(param.getContainerCode(), param.getAbc(),
                container, storageArea, startLocation, pickArea, ManualStockWork.ManualStockWorkType.out);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean in(ManualInParam param) throws BusinessException {
        Document set = getSet();
        String storageArea;
        String pickArea;
        String containerCode;
        Document container;
        Document startLocation;

        if (B.equals(param.getAbc())) {
            storageArea = set.getString("storageAreaB");
            // B类物料CTU任务下架任务需要固定指定终点库区为  B类原材料拣货入口库区
            pickArea = AreaCodeDict.bMatPickingExit.getText();
            startLocation = simpleFindOne("entity_" + STOCK_LOCATION, "area", pickArea);
            if (startLocation == null) {
                throw new BusinessException(String.format("库区[%s]下无库位", pickArea));
            }
            containerCode = param.getContainerCode();
            if (StringUtils.isBlank(containerCode)) {
                throw new BusinessException(String.format("请扫描容器码"));
            }
        } else {
            // A类处理逻辑
            storageArea = set.getString("storageArea");
            pickArea = set.getString("pickArea");
            String locationCode = param.getLocationCode();

            // 查询库位信息
            Query query = Query.query(newCriteria().and("code").is(locationCode)
                    .and("area").is(pickArea));
            startLocation = mongoTemplate.findOne(query, Document.class, "entity_" + STOCK_LOCATION);
            if (startLocation == null) {
                throw new BusinessException("请扫描正确的地库码");
            }
            boolean lock = startLocation.getBoolean("lock");
            if (lock) {
                throw new BusinessException("该库位已被锁定");
            }

            // 查询容器
            containerCode = startLocation.getString("containerCode");
            if (StringUtils.isBlank(containerCode)) {
                throw new BusinessException(String.format("此地库[%s]没有容器", locationCode));
            }
        }

        // 校验
        ManualStockWork one = mongoTemplate.findOne(Query.query(newCriteria().and("containerCode").is(containerCode)
                        .and("status").ne(ManualStockWork.ManualStockWorkStatus.finished.name())), ManualStockWork.class,
                "entity_" + MANUAL_IN_OUT_WORK);
        if (one != null) {
            throw new BusinessException(String.format("容器[%s]已存在未完成的上架任务，不能重复提交", containerCode));
        }

        container = getContainer(containerCode);
        UserVO apiUser = getApiUser();

        // 更新手动出入库物料信息
        ManualInOutMaterial manualInOutMaterial = mongoTemplate.findOne(Query.query(newCriteria().and("containerCode").is(containerCode)),
                ManualInOutMaterial.class, "entity_" + MANUAL_IN_OUT_MATERIAL);
        if (manualInOutMaterial == null) {
            manualInOutMaterial = new ManualInOutMaterial();
            manualInOutMaterial.setContainerCode(containerCode);
            manualInOutMaterial.setMaterialCode(param.getMaterialCode());
            manualInOutMaterial.setMaterialName(param.getMaterialName());
            manualInOutMaterial.setNum(param.getNum());
            manualInOutMaterial.setBatchNo(param.getBatchNo());
            manualInOutMaterial.setInTime(new Date());
            manualInOutMaterial.setNote(param.getNote());
            manualInOutMaterial.setCreateId(apiUser.createId);
            manualInOutMaterial.setCreateName(apiUser.createName);
            manualInOutMaterial.setTenantId(tenantId);
            mongoTemplate.insert(manualInOutMaterial, "entity_" + MANUAL_IN_OUT_MATERIAL);
        } else {
            Update update = new Update();
            update.set("materialCode", param.getMaterialCode());
            update.set("materialName", param.getMaterialName());
            update.set("num", param.getNum());
            update.set("batchNo", param.getBatchNo());
            update.set("inTime", new Date());
            update.set("note", param.getNote());
            mongoTemplate.updateFirst(Query.query(Criteria.where("_id").is(manualInOutMaterial.get_id())), update,
                    "entity_" + MANUAL_IN_OUT_MATERIAL);
        }

        // 调度wcs更新料箱状态
        if (B.equals(param.getAbc())) {
            Map<String, Object> params = new HashMap<>();
            params.put("podCode", containerCode);
            params.put("interactionState", "66c2dbfc7ba0581acbe7c47a");
            BaseResponse<Object> objectBaseResponse = wcsService.updateBoxState(params);
            if (!objectBaseResponse.isOk()) {
                throw new BusinessException("调用WCS更新料箱状态失败");
            }
        }
        return doMove(containerCode, param.getAbc(),
                container, pickArea, startLocation, storageArea, ManualStockWork.ManualStockWorkType.in);
    }

    public boolean doMove(String containerCode, String abc, Document container,
                          String startArea, Document startLocation,
                          String endArea, ManualStockWork.ManualStockWorkType manualStockWorkType) throws BusinessException {
        // 手动入库需要查找有满足的库位
        String endLocationCode = null;
        if (manualStockWorkType.equals(ManualStockWork.ManualStockWorkType.in)) {
            List<Document> endLocations = stockLocationService.queryEmptyLocation(Collections.singletonList(endArea), false);
            if (CollectionUtils.isEmpty(endLocations)) {
                throw new BusinessException(String.format("库区[%s]没有空余的库位", endArea));
            }
            Document endLocation = endLocations.get(0);
            assertBiz(() -> A.equals(abc) && endLocation.size() <= 4, "A库库位不足以分配");
            endLocationCode = endLocation.getString("code");

            // 锁定库位
            Update update = new Update();
            update.set("lock", true);
            if (!versionUpdateById(endLocation, update, "entity_" + STOCK_LOCATION)) {
                // 更新失败，需要重新申请
                throw new BusinessException("库位上锁失败，请重新下发");
            }
        }


        // 创建手动出库任务
        ManualStockWork manualStockWork = new ManualStockWork();
        manualStockWork.setCode(new ObjectId().toString());
        manualStockWork.setContainerCode(containerCode);
        manualStockWork.setStartArea(startArea);
        manualStockWork.setStartLocation(startLocation.getString("code"));
        manualStockWork.setEndArea(endArea);
        manualStockWork.setEndLocation(endLocationCode);
        manualStockWork.setType(manualStockWorkType.name());
        manualStockWork.setAbc(abc);
        manualStockWork.setTenantId(tenantId);
        mongoTemplate.insert(manualStockWork, "entity_" + MANUAL_IN_OUT_WORK);

        // 锁定存储库位与容器
        Update update = new Update();
        update.set("lock", true);
        if (!versionUpdateById(container, update, "entity_" + CONTAINER)) {
            // 更新失败，需要重新申请
            throw new BusinessException("容器上锁失败，请重新下发");
        }
        if (!versionUpdateById(startLocation, update, "entity_" + STOCK_LOCATION)) {
            // 更新失败，需要重新申请
            throw new BusinessException("库位上锁失败，请重新下发");
        }

        String taskType;
        if (manualStockWorkType.equals(ManualStockWork.ManualStockWorkType.in)) {
            taskType = TaskType.STOCK_IN.getId();
        } else {
            taskType = TaskType.STOCK_OUT.getId();
        }

        // 创建下架任务
        Task task = Task.builder()
                .taskCode(UUID.randomUUID().toString().replaceAll("-", ""))
                .taskType(taskType)
                .startLoc(startLocation.getString("code"))
                .endArea(endArea)
                .endLoc(endLocationCode)
                .podCode(containerCode)
                .business(MANUAL_IN_OUT_BIZ)
                .operationNo(Collections.singletonList(manualStockWork.getCode()))
                .build();
        wmsTaskService.saveTaskInfo(task);

        return true;
    }


    @Override
    public void handleCallback(String taskCode, String method, String locCode, String podCode) {
        log.info("处理任务回调信息:taskCode[{}],method[{}],locCode[{}],podCode[{}]", taskCode, method, locCode, podCode);

        List<AggregationOperation> operations = new ArrayList<>();
        operations.add(Aggregation.match(Criteria.where("tenantId").is(tenantId).and("isDelete").ne(YesOrNo.YES.ordinal())
                .and("taskCode").is(taskCode)));
        operations.add(Aggregation.addFields()
                .addFieldWithValue("taskType", ConvertOperators.ToString.toString("$taskType.id"))
                .addFieldWithValue("status", ConvertOperators.ToString.toString("$status.id"))
                .addFieldWithValue("performStatus", ConvertOperators.ToString.toString("$performStatus.id"))
                .build());

        AggregationResults<Task> results = mongoTemplate.aggregate(Aggregation.newAggregation(operations), StockOutConstants.TASK_TEMPLATE, Task.class);
        if (ObjectUtils.isEmpty(results.getMappedResults())) {
            return;
        }
        Task task = results.getMappedResults().get(0);
        if (TaskMethod.OUTBIN.getAction().equals(method)) {
            // 处理离开起点
            handleOutBin(taskCode, method, locCode, podCode, task);
        } else if (TaskType.STOCK_OUT.getId().equals(task.getTaskType()) && TaskMethod.FINISH.getAction().equals(method)) {
            // 容器到达分拣区
            containerArrive(taskCode, method, locCode, podCode, task);
        } else if (TaskType.STOCK_IN.getId().equals(task.getTaskType()) && TaskMethod.FINISH.getAction().equals(method)) {
            // 容器到达仓库
            containerInStock(taskCode, method, locCode, podCode, task);
        }
    }

    @Override
    public Object handleErrorCallback(TaskErrorCallbackVO errorCallbackVO) throws BusinessException {
        ErrorCodeEnum errorCodeEnum = ErrorCodeEnum.getByCode(errorCallbackVO.getErrorCode());
        if (ObjectUtils.isEmpty(errorCodeEnum)) {
            log.error("异常码不在预期范围内:errorCode[{}]", errorCallbackVO.getErrorCode());
            throw new BusinessException("异常码不在预期范围内");
        }

        Task task = wmsTaskService.findTaskByTaskCode(errorCallbackVO.getTaskCode());
        if (ObjectUtils.isEmpty(task)) {
            log.error("异常任务信息不存在:taskCode[{}]", errorCallbackVO.getTaskCode());
            throw new BusinessException("异常任务信息不存在");
        }

        Object result = handleCommonError(task);

//        switch (errorCodeEnum) {
//            case DOUBLE_IN:
//                result = doubleIn(task);
//                break;
//            case DOUBLE_IN2:
//                result = doubleIn2(task);
//                break;
//            case FETCHING_NOTHING:
//                result = fetchingNothing(task);
//                break;
//            case FETCHING_NOTHING2:
//                result = fetchingNothing2(task);
//        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    public String handleCommonError(Task task) throws BusinessException {
        List<String> workCodes = task.getOperationNo();
        ManualStockWork work = mongoTemplate.findOne(Query.query(newCriteria().and("code").in(workCodes)),
                ManualStockWork.class, "entity_" + MANUAL_IN_OUT_WORK);
        String type = work.getType();
        if (ManualStockWork.ManualStockWorkType.out.name().equals(type)) {
            // 下架任务取消
            return cancelWork(work);
        } else {
            // 发生在分拣后重新查找库位
            return reApplyLocation(work);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public String reApplyLocation(ManualStockWork work) throws BusinessException {
        Document set = getSet();
        String storageArea = set.getString("storageArea");
        List<Document> locations = stockLocationService.queryEmptyLocation(Collections.singletonList(storageArea), false);
        if (CollectionUtils.isEmpty(locations)) {
            return cancelWork(work);
        } else if (A.equals(work.getAbc()) && locations.size() <= 5) {
            return cancelWork(work);
        } else {
            Document fl = locations.get(0);
            String locationCode = fl.getString("code");
            Update update = new Update();
            update.set("locationCode", locationCode);
            update.set("areaCode", fl.getString("area"));
            update.set("updateTime", new Date());
            updateById(work.get_id().toString(), update, "entity_" + MANUAL_IN_OUT_WORK);
            return locationCode;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public String cancelWork(ManualStockWork work) {
        Update delete = new Update();
        delete.set("isDelete", 1);
        updateById(work.get_id().toString(), delete, "entity_" + MANUAL_IN_OUT_WORK);
        return "NULL";
    }

    @Override
    public List<Document> list(ManualInParam param) {
        Document re = simpleFindOne("entity_" + MANUAL_IN_OUT_SET);
        if (re == null) {
            return Collections.emptyList();
        }
        String storageArea;
        if (B.equals(param.getAbc())) {
            storageArea = re.getString("storageAreaB");
        } else {
            storageArea = re.getString("storageArea");
        }
        if (StringUtils.isBlank(storageArea)) {
            return Collections.emptyList();
        }
        Criteria criteria = newCriteria().and("area").is(storageArea).and("lock").is(false);
        if (!StringUtils.isAllBlank(param.getMaterialCode(), param.getMaterialName(), param.getBatchNo(), param.getNote())) {
            Criteria containerMMaterialCriteria = newCriteria();
            if (StringUtils.isNoneBlank(param.getMaterialCode())) {
                containerMMaterialCriteria.and("materialCode").regex(param.getMaterialCode());
            }
            if (StringUtils.isNoneBlank(param.getMaterialName())) {
                containerMMaterialCriteria.and("materialName").regex(param.getMaterialName());
            }
            if (StringUtils.isNoneBlank(param.getBatchNo())) {
                containerMMaterialCriteria.and("batchNo").regex(param.getBatchNo());
            }
            if (StringUtils.isNoneBlank(param.getNote())) {
                containerMMaterialCriteria.and("note").regex(param.getNote());
            }
            List<ManualInOutMaterial> manualInOutMaterials = mongoTemplate.find(Query.query(containerMMaterialCriteria), ManualInOutMaterial.class,
                    "entity_" + MANUAL_IN_OUT_MATERIAL);
            if (CollectionUtils.isEmpty(manualInOutMaterials)) {
                return Collections.emptyList();
            }
            List<String> containerCodes = manualInOutMaterials.stream().map(x -> x.getContainerCode()).collect(Collectors.toList());
            criteria = criteria.and("containerCode").in(containerCodes);
        } else {
            criteria = criteria.and("containerCode").exists(true).ne(null);
        }

        List<AggregationOperation> operations = new LinkedList<>();
        operations.add(Aggregation.match(criteria));
        String sql = "{ $lookup:{ " +
                "from:'entity_"+MANUAL_IN_OUT_MATERIAL+"'," +
                "let:{ containerCode:'$containerCode'}," +
                "pipeline:[" +
                "{$match:{$and:[{$expr:{$eq:['$containerCode','$$containerCode']}},{$expr:{$eq:['$isDelete',0]}}]}}" +
                "]," +
                "as:'details'}}";
        operations.add(new CustomAggregationOperation(sql));
//        operations.add(Aggregation.lookup("entity_" + MANUAL_IN_OUT_MATERIAL, "containerCode", "containerCode", "details"));
//        operations.add(Aggregation.unwind("detail"));

        List<Document> documents = aggregateResult(operations, "entity_" + STOCK_LOCATION);
        return documents.stream().filter(x -> StringUtils.isNoneBlank(x.getString("containerCode"))).collect(Collectors.toList());
    }

    @Override
    public boolean bind(String locationCode, String containerCode) throws BusinessException {
        Document container = simpleFindOne("entity_" + CONTAINER, "code", containerCode);
        if (container == null) {
            throw new BusinessException(String.format("容器[%s]不存在", containerCode));
        }
        if (container.getBoolean("lock")) {
            throw new BusinessException(String.format("容器[%s]被锁定", containerCode));
        }
        Document locationCheck = simpleFindOne("entity_" + STOCK_LOCATION, "containerCode", containerCode);
        if (locationCheck != null) {
            throw new BusinessException(String.format("容器[%s]已绑定在库位[%s]中", containerCode, locationCheck.getString("code")));
        }

        Document location = simpleFindOne("entity_" + STOCK_LOCATION, "code", locationCode);
        if (location == null) {
            throw new BusinessException(String.format("库位[%s]不存在", containerCode));
        }
        if (location.getBoolean("lock")) {
            throw new BusinessException(String.format("库位[%s]被锁定", containerCode));
        }
        if (StringUtils.isNoneBlank(location.getString("containerCode"))) {
            throw new BusinessException(String.format("库位[%s]已有容器", containerCode));
        }

        Update update = new Update();
        update.set("containerCode", containerCode);
        update.set("updateTime", new Date());
        return updateById(location, update, "entity_" + STOCK_LOCATION);
    }

    @Override
    public Object detail(ManualInParam param) throws BusinessException {
        String containerCode = param.getContainerCode();
        if (B.equals(param.getAbc())) {
            if (StringUtils.isBlank(containerCode)) {
                throw new BusinessException("缺少容器编码");
            }
        } else {
            String locationCode = param.getLocationCode();
            if (StringUtils.isBlank(locationCode)) {
                return null;
            }
            // 查询库位信息
            Query query = Query.query(newCriteria().and("code").is(locationCode));
            Document startLocation = mongoTemplate.findOne(query, Document.class, "entity_" + STOCK_LOCATION);
            if (startLocation == null) {
                throw new BusinessException(String.format("请扫描正确的地库码"));
            }
            boolean lock = startLocation.getBoolean("lock");
            if (lock) {
                throw new BusinessException("该库位已被锁定");
            }
            // 查询容器
            containerCode = startLocation.getString("containerCode");
        }

        // 验证
        Document container = mongoTemplate.findOne(Query.query(newCriteria().and("code").is(containerCode)), Document.class, "entity_" + CONTAINER);
        if (container == null) {
            throw new BusinessException(String.format("容器[%s]不存在", containerCode));
        }
        if (container.getBoolean("lock")) {
            throw new BusinessException(String.format("容器[%s]被锁定", containerCode));
        }
        return detailByContainerCode(containerCode);
    }

    @Override
    public Document detailByContainerCode(String containerCode) throws BusinessException {
        return mongoTemplate.findOne(Query.query(newCriteria().and("containerCode").is(containerCode)),
                Document.class, "entity_" + MANUAL_IN_OUT_MATERIAL);
    }

//    @Override
//    public void analysisExport(HttpServletResponse response) throws BusinessException {
//        List<Map> mapList = new LinkedList<>();
//        List<Document> documents = mongoTemplate.find(Query.query(newCriteria()), Document.class, "entity_" + MANUAL_IN_OUT_MATERIAL);
//        Set<String> mCodeList = new HashSet<>();
//        Set<String> cCodeList = new HashSet<>();
//        Map<String, Integer> containerBindMaterialCount = new HashMap<>();
//        documents.forEach(x -> {
//            String mc = x.getString("materialCode");
//            String cc = x.getString("containerCode");
//            mCodeList.add(mc);
//            cCodeList.add(cc);
//            String key = mc + ":" + cc;
//            containerBindMaterialCount.put(key, containerBindMaterialCount.getOrDefault(key, 0) + 1);
//        });
//
//        // 物料信息
//        List<Document> mList = mongoTemplate.find(Query.query(newCriteria().and("code").in(mCodeList)),
//                Document.class, "entity_" + MATERIAL);
//        Map<String, List<Document>> mMap = new HashMap<>();
//        mList.forEach(x -> {
//            mMap.computeIfAbsent(x.getString("code"), k -> new ArrayList<>()).add(x);
//        });
//
//        // 物料存储规则
//        List<Document> ruleList = mongoTemplate.find(Query.query(newCriteria().and("materialCode").in(mCodeList)),
//                Document.class, "entity_" + STOCK_RULE);
//        Map<String, Document> ruleMap = ruleList.stream().collect(Collectors.toMap(x -> x.getString("materialCode"), x -> x, (x1, x2) -> x1));
//
//        // 容器信息
//        List<Document> cList = mongoTemplate.find(Query.query(newCriteria().and("code").in(cCodeList)),
//                Document.class, "entity_" + CONTAINER);
//        Map<String, Document> cMap = cList.stream().collect(Collectors.toMap(x -> x.getString("code"), x -> x));
//
//        // 容器库位信息
//        List<Document> lList = mongoTemplate.find(Query.query(newCriteria().and("containerCode").in(cCodeList)),
//                Document.class, "entity_" + STOCK_LOCATION);
//        Map<String, List<Document>> clListMap = new HashMap<>();
//        lList.stream().forEach(x -> clListMap.computeIfAbsent(x.getString("containerCode"), k -> new ArrayList<>()).add(x));
//
//        for (Document x : documents) {
//            Map<String, Object> m = new LinkedHashMap<>();
//            mapList.add(m);
//            m.put("容器编码", x.get("containerCode"));
//            m.put("物料编码", x.get("materialCode"));
//            m.put("物料名称", "");
//            m.put("数量", x.get("num"));
//            m.put("批次", x.get("batchNo"));
//            m.put("时间", ConvertUtils.convertIfPossible(x.get("inTime"), String.class));
//            m.put("库位", "");
//            m.put("其他备注", x.get("note"));
//
//            String mc = x.getString("materialCode");
//            if (StringUtils.isBlank(mc)) {
//                m.put("结果", "物料不存在");
//                continue;
//            }
//            List<Document> materials = mMap.get(mc);
//            if (CollectionUtils.isEmpty(materials)) {
//                m.put("结果", "物料不存在");
//                continue;
//            }
//            if (materials.size() > 1) {
//                m.put("结果", "物料数据存在多份");
//                continue;
//            }
//            Document material = materials.get(0);
//            m.put("物料名称", material.getString("materialName"));
//
//            Document rule = ruleMap.get(mc);
//            if (rule == null) {
//                m.put("结果", "物料存储规则不存在");
//                continue;
//            }
//            String cc = x.getString("containerCode");
//            if (StringUtils.isBlank(mc)) {
//                m.put("结果", "容器编码不存在");
//                continue;
//            }
//            Document container = cMap.get(cc);
//            if (container == null) {
//                m.put("结果", "容器编码不存在");
//                continue;
//            }
//
//            // 库位信息
//            List<Document> locations = clListMap.get(cc);
//            if (CollectionUtils.isEmpty(locations)) {
//                m.put("结果", "无对应库位");
//                continue;
//            } else if (locations.size() > 1) {
//                m.put("结果", "所在库位异常，一个容器不能在两个库位中");
//                continue;
//            } else {
//                m.put("库位", locations.get(0).getString("code"));
//            }
//
//            int count = containerBindMaterialCount.get(mc + ":" + cc);
//            if(count > 1) {
//                String typeId = container.getEmbedded(Arrays.asList("type", "id"), String.class);
//                if(count == 2 && !partitionBox.getCode().equals(typeId)) {
//                    m.put("结果", "容器混放两种物料，但是容器类型不支持");
//                }
//                else if(count == 3 && !partitionBox3.getCode().equals(typeId)) {
//                    m.put("结果", "容器混放3种物料，但是容器类型不支持");
//                }
//                else if(count == 4 && !partitionBox4.getCode().equals(typeId)) {
//                    m.put("结果", "容器混放4种物料，但是容器类型不支持");
//                } else {
//                    m.put("结果", String.format("容器混放%d种物料，但是容器类型不支持", count));
//                }
//            }
//        }
//        ExcelUtil.exportToExcel(response, "隔离区物料分析.xls", mapList, true);
//    }

    @Transactional(rollbackFor = Exception.class)
    public void handleOutBin(String taskCode, String method, String locCode, String podCode, Task task) {
        List<String> operationNo = task.getOperationNo();
        if (!CollectionUtils.isEmpty(operationNo)) {
            Update update = new Update();
            update.set("status", ManualStockWork.ManualStockWorkStatus.running.name());
            update.set("updateTime", new Date());
            mongoTemplate.updateMulti((Query.query(newCriteria().and("code").in(operationNo))), update, "entity_" + MANUAL_IN_OUT_WORK);
        }

        // 处理离开起点
        // 起点库位解绑容器，并解锁
        Update update = new Update();
        update.unset("containerCode");
        update.set("lock", false);
        UpdateResult updateResult = mongoTemplate.updateFirst(Query.query(newCriteria().and("code").is(locCode)), update,
                "entity_" + STOCK_LOCATION);
        log.info("任务离开起点节点回调,更新库位[{}]绑定的容器[{}],更新结果[{}]", locCode, update, updateResult);
    }

    @Transactional(rollbackFor = Exception.class)
    public void containerArrive(String taskCode, String method, String locCode, String podCode, Task task) {
        List<String> operationNo = task.getOperationNo();
        String abc = null;
        if (!CollectionUtils.isEmpty(operationNo)) {
            Update update = new Update();
            update.set("status", ManualStockWork.ManualStockWorkStatus.finished.name());
            update.set("endLocation", locCode);
            update.set("updateTime", new Date());
            mongoTemplate.updateMulti((Query.query(newCriteria().and("code").in(operationNo))), update, "entity_" + MANUAL_IN_OUT_WORK);

            // 下架到达分拣台入口的时候，不需要绑定库位与料箱
            List<ManualStockWork> inStockTasks = mongoTemplate.find(Query.query(newCriteria().and("code").in(task.getOperationNo())),
                    ManualStockWork.class, "entity_" + MANUAL_IN_OUT_WORK);
            if (!CollectionUtils.isEmpty(inStockTasks)) {
                abc = inStockTasks.get(0).getAbc();
            }
        }

        // 解锁容器
        unlockContainer(podCode);
        // 容器到达分拣区
        Update update = new Update();
        update.set("lock", false);
        if (!B.equals(abc)) {
            update.set("containerCode", podCode);
        }
        UpdateResult updateResult = mongoTemplate.updateFirst(Query.query(newCriteria().and("code").is(locCode)),
                update, "entity_" + STOCK_LOCATION);

        log.info("任务离开起点到达分拣区,更新库位[{}]绑定的容器[{}],更新结果[{}]", locCode, update, updateResult);
    }

    @Transactional(rollbackFor = Exception.class)
    public void containerInStock(String taskCode, String method, String locCode, String podCode, Task task) {
        List<String> operationNo = task.getOperationNo();
        if (!CollectionUtils.isEmpty(operationNo)) {
            Update update = new Update();
            update.set("status", ManualStockWork.ManualStockWorkStatus.finished.name());
            update.set("updateTime", new Date());
            mongoTemplate.updateMulti((Query.query(newCriteria().and("code").in(operationNo))), update, "entity_" + MANUAL_IN_OUT_WORK);
        }

        // 解锁容器
        unlockContainer(podCode);
        // 库位与容器绑定并解锁库位
        Update update3 = new Update();
        update3.set("lock", false);
        update3.set("containerCode", podCode);
        update3.set("updateTime", new Date());
        mongoTemplate.updateFirst((Query.query(newCriteria().and("code").is(locCode))), update3, "entity_" + STOCK_LOCATION);

    }

    private void unlockContainer(String containerCode) {
        Update update2 = new Update();
        update2.set("lock", false);
        update2.set("updateTime", new Date());
        mongoTemplate.updateFirst((Query.query(newCriteria().and("code").is(containerCode))), update2, "entity_" + CONTAINER);
    }

    private Document getContainer(String containerCode) throws BusinessException {
        Document container = simpleFindOne("entity_" + CONTAINER, "code", containerCode);
        if (container == null) {
            throw new BusinessException(String.format("容器[%s]不存在", containerCode));
        }
        if (container.getBoolean("lock")) {
            throw new BusinessException(String.format("容器[%s]已被锁定", containerCode));
        }
        return container;
    }

    private Document getSet() throws BusinessException {
        Document re = simpleFindOne("entity_" + MANUAL_IN_OUT_SET);
        if (re == null) {
            throw new BusinessException("请先配置手动出入库");
        }
        String storageArea = re.getString("storageArea");
        if (StringUtils.isBlank(storageArea)) {
            throw new BusinessException("未配置存储区域");
        }
        String pickArea = re.getString("pickArea");
        if (StringUtils.isBlank(pickArea)) {
            throw new BusinessException("未配置拣货区域");
        }
        String storageAreaB = re.getString("storageAreaB");
        if (StringUtils.isBlank(storageAreaB)) {
            throw new BusinessException("未配置B类存储区域");
        }
        return re;
    }


}
